﻿using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
using Loong.Logging;

namespace Loong.Threading.BackgroundWorkers
{
    public abstract class PeriodicBackgroundWorkerBase : RunnableBase, IBackgroundWorker
    {
        private Task _executingTask;
        private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();

        protected ILogger Logger { get; }

        protected TimeSpan Period { get; set; } = TimeSpan.FromSeconds(5);

        protected PeriodicBackgroundWorkerBase(ILogger logger)
        {
            Logger = logger;
        }

        public override void Start()
        {
            base.Start();
            _executingTask = ExecuteAsync(_stoppingCts.Token);

            Logger.LogDebug($"Start background worker: {ToString()}");
        }

        public override void Stop()
        {
            if (_executingTask != null && !_stoppingCts.IsCancellationRequested)
            {
                _stoppingCts.Cancel();
            }
            base.Stop();

            Logger.LogDebug($"Stop background worker: {ToString()}");
        }

        public override void WaitToStop()
        {
            if (_executingTask != null)
            {
                try
                {
                    if (!_stoppingCts.IsCancellationRequested)
                    {
                        _stoppingCts.Cancel();
                    }
                }
                finally
                {
                    Task.WaitAny(new[] { _executingTask }, TimeSpan.FromMinutes(1));
                }
            }

            base.WaitToStop();

            Logger.LogDebug($"WaitToStop background worker: {ToString()}");
        }

        private async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (IsRunning && !stoppingToken.IsCancellationRequested)
            {
                try
                {
                    await DoWorkAsync();

                    await Task.Delay(Period, stoppingToken);
                }
                catch (Exception ex)
                {
                    Logger.LogException(ex);
                }
            }
        }

        protected abstract Task DoWorkAsync();
    }
}
